package com.IOC.beans;

import org.apache.commons.lang3.StringUtils;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * bean工厂默认实现类（实现BeanFactory和BeanDefinitionRegistry，并且可关闭）
 */
public class DefaultBeanFactory implements BeanFactory, BeanDefinitionRegistry, Closeable {

    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultBeanFactory.class);

    // bean定义对象Map
    protected Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    // 单例bean实例Map
    private Map<String, Object> singletonBeanMap = new ConcurrentHashMap<>(256);

    // bean实例别名Map
    private Map<String, String[]> aliasMap = new ConcurrentHashMap<>(256);

    // bean类型type与beanName之间的一对多映射Map
    private Map<Class<?>, Set<String>> typeMap = new ConcurrentHashMap<>(256);

    /**
     * 注册bean定义
     * @param beanName bean名称
     * @param beanDefinition bean定义对象
     * @throws BeanDefinitionRegistException bean注册异常
     */
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionRegistException {
        /*
          问题1：如何存储BeanDefinition？  Map<String, BeanDefinition>
          问题2：beanName重名怎么办？    抛出异常
          问题3：这里代码逻辑有哪些？     判空、验证beanDefinition合法性、判断重名、放入map
         */

        // 判空
        Objects.requireNonNull(beanName, "注册bean必须提供beanName");
        Objects.requireNonNull(beanDefinition, "注册bean必须提供beanDefinition对象");

        // 校验bean定义是否合法
        if(!beanDefinition.validate()) {
            throw new BeanDefinitionRegistException("名字为["+ beanName +"]的bean定义不合法: " + beanDefinition);
        }

        // 判断beanName是否重名（重名则默认抛异常）
        // Spring中可通过设置spring.main.allow-bean-definition-overriding: true 来允许覆盖
        if(this.containsBeanDefinition(beanName)) {
            throw new BeanDefinitionRegistException("名字为["+ beanName +"]的bean已存在：" + this.getBeanDefinition(beanName));
        }

        // 放入beanDefinitionMap中
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }

    /**
     * 获取bean定义
     * @param beanName bean名称
     * @return bean定义对象
     */
    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        return null;
    }

    /**
     * 判断是否包含bean定义
     * @param beanName bean名称
     * @return 是否包含bean定义对象
     */
    @Override
    public Boolean containsBeanDefinition(String beanName) {
        return null;
    }

    /**
     * 获取bean对象
     * @param beanName bean名称
     * @return bean实例对象
     * @throws Exception 通用异常
     */
    @Override
    public Object getBean(String beanName) throws Exception {
        /*
          需要实现哪些逻辑
         */
        return doGetBean(beanName);

    }

    /**
     * 根据bean名称创建bean实例
     * @param beanName bean名称
     * @return bean实例
     * @throws Exception 通用异常
     */
    private Object doGetBean(String beanName) throws Exception {
        // 判断bean名称和bean定义对象是否为空，不为空则开始构造bean实例
        Objects.requireNonNull(beanName, "beanName不能为空");

        // 实现单例模式创建bean实例
        Object instance = singletonBeanMap.get(beanName);
        if(instance != null) {
            return instance;
        }

        BeanDefinition bd = this.getBeanDefinition(beanName);
        Objects.requireNonNull(bd, "beanDefinition不能为空");

        // 实现单例模式创建bean实例
        if(bd.isSingleton()) {
            synchronized (this.singletonBeanMap) {
                instance = this.singletonBeanMap.get(beanName);
                if(instance == null) {  // 第二次检查
                    instance = doCreateInstance(bd);
                    this.singletonBeanMap.put(beanName, instance);
                }
            }
        } else {
            instance = doCreateInstance(bd);
        }

        return instance;
    }

    /**
     * 根据bean定义对象创建bean实例
     * @param bd bean定义对象
     * @return bean实例
     * @throws Exception 通用异常
     */
    private Object doCreateInstance(BeanDefinition bd) throws Exception {
        Class<?> type = bd.getBeanClass();
        // 三种方式创建bean实例，根据type和是否存在工厂方法选择使用哪种方式创建对象
        Object instance = null;
        if(type != null) {
            if(StringUtils.isBlank(bd.getFactoryMethodName())) {
                // 构造器方法来构造对象
                instance = this.createInstanceByConstructor(bd);
            } else {
                // 静态工厂创建对象
                instance = this.createInstanceByStaticFactoryMethod(bd);
            }
        } else {
            // 通过工厂bean方式创建对象
            instance = this.createInstanceByFactoryBean(bd);
        }

        // 初始化
        doInit(bd, instance);

        return instance;
    }

    /**
     * 通过构造器方法创建实例
     * @param bd bean定义对象
     * @return bean实例
     * @throws Exception 通用异常
     */
    private Object createInstanceByConstructor(BeanDefinition bd) throws Exception{
        try {
//            return bd.getBeanClass().newInstance();
            return bd.getBeanClass().getDeclaredConstructor().newInstance();
        } catch(SecurityException e) {
            log.error("构造器创建bean实例异常，beanDefinition: " + bd, e);
            throw e;
        }
    }

    /**
     * 通过静态工厂创建实例
     * @param bd bean定义对象
     * @return bean实例
     * @throws Exception 通用异常
     */
    private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception {
        Class<?> type = bd.getBeanClass();
        Method m = type.getMethod(bd.getFactoryMethodName());
        return m.invoke(type);
    }

    /**
     * 通过工厂bean实例创建实例
     * @param bd bean定义对象
     * @return bean实例
     * @throws Exception 通用异常
     */
    private Object createInstanceByFactoryBean(BeanDefinition bd) throws Exception {
        Object factoryBean = this.doGetBean(bd.getFactoryBeanName());
        Method m = factoryBean.getClass().getMethod(bd.getFactoryMethodName());
        return m.invoke(factoryBean);
    }

    /**
     * 执行初始化流程
     * @param bd bean定义对象
     * @param instance bean实例
     * @throws Exception 通用异常
     */
    private void doInit(BeanDefinition bd, Object instance) throws Exception {
        if(StringUtils.isNotBlank(bd.getInitMethodName())) {
            Method m = instance.getClass().getMethod(bd.getInitMethodName());
            m.invoke(instance);
        }
    }

    /**
     * 关闭工厂
     * @throws IOException 关闭IO异常
     */
    @Override
    public void close() throws IOException {
        // 执行单例实例的销毁方法
        for(Map.Entry<String, BeanDefinition> e: this.beanDefinitionMap.entrySet()) {
            String beanName = e.getKey();
            BeanDefinition bd = e.getValue();

            if(bd.isSingleton() && StringUtils.isNotBlank(bd.getDestroyMethodName())) {
                Object instance = this.singletonBeanMap.get(beanName);
                Method m = null;
                try {
                    m = instance.getClass().getMethod(bd.getDestroyMethodName());
                    m.invoke(instance);
                } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
//                    throw new RuntimeException(ex);
                    log.error("执行bean[" + beanName + "]" + bd + "的销毁方法异常！", ex);
                }
            }
        }
        /*
           疑问：原型bean为什么不需要销毁？ 因为原型bean不需要缓存bean，没有引用对象引用的时候，会由GC自行回收，就不需要实行销毁。
        */

    }

    /*
        ----------------------------------------- getBeanByType接口 ---------------------------------------
     */

    @Override
    public <T> T getBean(Class<T> type) throws Exception {
        /*
            步骤：
            1、获取其对应的所有BeanName和BeanDefinition
            2、如果只有一个beanName与之对应则直接返回该bean实例，否则
            3、遍历所有beanName，找到标记为primary的beanName并返回该实例
            4、如果primary的bean没有或多于一个，抛出异常
        */
        Set<String> beanNameSet = this.typeMap.get(type);
        if(beanNameSet.size() == 1) {
            return (T) this.getBean(beanNameSet.iterator().next());
        } else {
            BeanDefinition bd = null;
            String primaryName = null;
            StringBuilder builder = new StringBuilder();
            for (String beanName : beanNameSet) {
                bd = this.getBeanDefinition(beanName);
                if(bd != null && bd.isPrimary()) {
                    // 找到了标记为primary的bean
                    if(primaryName != null) {
                        String message = type + "类型存在多个标记为primary的bean[" + primaryName + ", " + beanName + "]";
                        log.error(message);
                        throw new Exception(message);
                    } else {
                        primaryName = beanName;
                    }
                }
                builder.append(" ").append(beanName);
            }

            if(primaryName != null) {
                return (T) this.getBean(primaryName);
            } else {
                String message = type + "类型存在多个bean[" + builder + "]，但无法确定primary";
                log.error(message);
                throw new Exception(message);
            }
        }
    }

    @Override
    public <T> Map<String, T> getBeanOfType(Class<T> type) throws Exception {
        return null;
    }

    @Override
    public <T> List<T> getBeanListOfType(Class<T> type) throws Exception {
        return null;
    }

    @Override
    public Class<?> getType(String beanName) throws Exception {
        BeanDefinition bd = this.getBeanDefinition(beanName);
        Class<?> type = bd.getBeanClass();
        // 根据构建实例的不同方式，返回class类型
        if(type != null) {
            if(StringUtils.isBlank(bd.getFactoryMethodName())) {
                // type就是beanClass
                return type;
            } else {
                //  静态工厂方式创建的bean，反射获取method并获取返回值类型
                type = type.getDeclaredMethod(bd.getFactoryMethodName()).getReturnType();
            }
        } else {
            // 工厂bean方式创建的bean，获取工厂bean的class，在获取工厂方法的返回值类型
            type = this.getType(bd.getFactoryBeanName());
            type = type.getDeclaredMethod(bd.getFactoryMethodName()).getReturnType();
        }

        return type;
    }

    /**
     * 构建type到beanName的映射关系（也叫buildTypeMap()）
     * 将当前类，所继承父类和所实现接口都映射到beanName
     * @throws Exception 通用异常
     */
    private void registerTypeMap() throws Exception {
        for(String beanName : this.beanDefinitionMap.keySet()) {
            Class<?> type = this.getType(beanName);
            // 映射本类
            this.registerTypeMap(beanName, type);

            // 映射父类
            this.registerSuperClassTypeMap(beanName, type);

            // 映射接口
            this.registerInterfaceTypeMap(beanName, type);
        }
    }

    /**
     * 构建type到beanName的映射关系（也叫buildTypeMap()）
     * @param beanName 将当前类映射到beanName
     * @param type class类型
     */
    private void registerTypeMap(String beanName, Class<?> type) {
        Set<String> beanNameSet = this.typeMap.get(type);
        if(beanNameSet == null) {
            // 初始化
            beanNameSet = new HashSet<>();
            this.typeMap.put(type, beanNameSet);
        }
        beanNameSet.add(beanName);
    }

    /**
     * 构建type的所有父类（除Object外）到beanName的映射关系
     * @param beanName bean名称
     * @param type  class类型名
     */
    private void registerSuperClassTypeMap(String beanName, Class<?> type) {
        Class<?> superclass = type.getSuperclass();
        if(superclass != null && !superclass.equals(Objects.class)) {
            // 注册父类
            this.registerTypeMap(beanName, type);
            // 注册父类所继承的父类
            this.registerSuperClassTypeMap(beanName, superclass);
            // 注册父类所实现的接口
            this.registerInterfaceTypeMap(beanName, superclass);
        }
    }

    /**
     * 构建接口到beanName的映射关系
     * @param beanName bean名称
     * @param type class类型
     */
    private void registerInterfaceTypeMap(String beanName, Class<?> type) {
        Class<?>[] interfaces = type.getInterfaces();
        for (Class<?> interf : interfaces){
            // 注册当前接口
            this.registerTypeMap(beanName, interf);
            // 注册父接口
            this.registerInterfaceTypeMap(beanName, interf);
        }
    }


    /*
        --------------------------------------------------------------------------
     */


    /*
        ----------------------------------------- 别名CRUD接口 ---------------------------------------
     */

    @Override
    public void registerAlias(String name, String alias) {
        String[] aliasArray = aliasMap.get(name);
        if(aliasArray == null) {
            aliasArray = new String[1];
            aliasArray[0] = alias;
        } else {
            int len = aliasArray.length;
            String[] aliasArrayTmp = new String[len + 1];
            int i = 0;
            for(; i < len; i++) {
                aliasArrayTmp[i] = aliasArray[i];
            }
            aliasArrayTmp[i] = alias;
            aliasArray = aliasArrayTmp;
        }
        aliasMap.put(name, aliasArray);
    }

    @Override
    public void removeAlias(String alias) {

    }

    @Override
    public boolean isAlias(String name) {
        return false;
    }

    @Override
    public String getOriginalName(String name) {
        return null;
    }

    @Override
    public String[] getAliases(String name) {
        return new String[0];
    }

    /*
        --------------------------------------------------------------------------
     */
}
